use std::{env, path::Path};

use anyhow::{Result, anyhow};
use once_cell::sync::{Lazy};

pub(crate) static APP_CONFIG: Lazy<AppConfig> = {
    Lazy::new(|| {
        let p = env::current_dir()
            .map(|mut p| {
                p.push("config");
                p.push("config.json");
                p
            })
            .expect("config path push failed!");
        let config = AppConfig::load_from_path(p).expect("load appconfig failed!");
        config
    })
};

#[allow(dead_code)]
#[derive(serde::Deserialize)]
pub struct AppConfig {
    log: LogConfig,
    database: String,
}

impl AppConfig {
    pub fn load_from_path(p: impl AsRef<Path>) -> Result<AppConfig> {
        let p = p.as_ref();
        if !p.exists() {
            return Err(anyhow!("file: {} is not exist!", p.display()));
        } else {
            let file_content = std::fs::read_to_string(p)?;
            let app: AppConfig = serde_json::from_str(&file_content)?;
            Ok(app)
        }
    }
    pub fn database(&self)->&str{
        &self.database
    }
}

#[allow(dead_code)]
#[derive(Debug, serde::Deserialize)]
struct LogConfig {
    level: LogLevel,
}
#[derive(Debug, serde::Deserialize, PartialEq)]
enum LogLevel {
    #[serde(rename = "info")]
    Info,
    #[serde(rename = "trace")]
    Trace,
    #[serde(rename = "error")]
    Error,
    #[serde(rename = "warning")]
    Warning,
}

#[cfg(test)]
mod test {
    use std::env;

    use super::*;
    use anyhow::Result;

    #[test]
    fn load_from_path_test() -> Result<()> {
        let p = env::current_dir().map(|mut p| {
            p.push("config");
            p.push("config.json");
            p
        })?;
        if !p.exists() {
            return Err(anyhow!("path: {} not exist", p.display()));
        }
        let appconfig = AppConfig::load_from_path(p)?;
        assert!(!appconfig.database.is_empty());
        assert!(appconfig.log.level.eq(&LogLevel::Info));
        Ok(())
    }
}
